package nio.book.channel;

import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;

/**
 * 通道之间复制数据
 */
public class ChannelCopy {
    public static void main(String[] args) throws Exception {
        ReadableByteChannel source = Channels.newChannel(System.in);
        WritableByteChannel dest = Channels.newChannel(System.out);
        channelCopy1(source, dest);
//        channelCopy2(source, dest);
        source.close();
        dest.close();
    }


    /**
     * 方法一:
     * 如果缓冲区未完全耗尽，此实现将在临时缓冲区上使用compact（）来打包数据。
     * 这可能会导致数据复制，但会最大限度地减少系统调用。
     * 它还需要一个清理循环来确保所有数据都被发送。
     */
    private static void channelCopy1(ReadableByteChannel src, WritableByteChannel dest) throws Exception {
        ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
        while (src.read(buffer) != -1) {
            buffer.flip();
            dest.write(buffer);
            //If buffer is empty, same as doing clear()
            buffer.compact();
        }
        // EOF will leave buffer in fill state
        buffer.flip();
        // Make sure that the buffer is fully drained
        while (buffer.hasRemaining()) {
            dest.write(buffer);
        }
    }

    /**
     * 方法二:
     * 此方法执行相同的复制，但在读取更多数据之前确保临时缓冲区为空。
     * 这从不需要数据复制，但可能导致更多的系统调用。
     * 不需要进行循环后清理，因为退出循环时缓冲区将为空。
     */
    private static void channelCopy2(ReadableByteChannel src, WritableByteChannel dest) throws Exception {
        ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
        while (src.read(buffer) != -1) {
            buffer.flip();
            // Make sure that the buffer was fully drained
            while (buffer.hasRemaining()) {
                dest.write(buffer);
            }
            // Make the buffer empty, ready for filling
            buffer.clear();
        }
    }
}
